home *** CD-ROM | disk | FTP | other *** search
-
-
- About the TblInfoDlg Component...
-
- The Delphi Component Writer's Guide recommends that you take
- frequently used dialogs and make them into components and
- that you create a 'wrapper' unit so that the form and it's
- associated resources are only loaded into memory when they're
- needed. They even provide an example with an 'About' dialog.
- Fine as far as it goes, but most dialogs are a little more
- complex. The TblInfoDlg component is a multi-page dialog that gives
- a user lots of information about a Paradox, DBase or ODBC table.
-
- This example (of sorts) consists of :
-
- The files for the wrapper unit:
- TBLINFO.PAS
- TBLINFO.DCU
- TBLINFO.DCR
-
- The files for the TblInfoDlg dialog:
- TBLDLG.PAS
- TDLIDLG.DCU
- TBLDLG.DFM
-
- The files for the sample program:
- DBIT.DPR
- DBIT.OPT
- DBIT.RES
- DBITEST.DCU
- DBITEST.DFM
- DBITEST.PAS
-
- To use all this, you have to install the TblInfoDlg component by
- adding tblinfo.pas to the component palette, where it will show up
- on the Data Controls page. Once this is done, the Dbit.dpr project
- file can be opened and run. What you'll see is a small form with four
- buttons-one pops up a File Open dialog that allows you to open a
- table, and the second one pops up the TblInfoDlg dialog with information
- about the aforementioned table, the third one closes the table and the
- fourth one closes the sample program. We at SJHDesign have found this
- component quite handy, it's been pretty well tested and is currently
- hard at work in real world applications at two client sites, so the
- code is(to our knowledge) solid.
-
-
- Using the TblInfoDlg component in your applications:
- _________________________________________________
-
- All you have to do to use this component is supply it with a
- TTable component. This can be done at design time, by using the
- Object Inspector to put the fully qualified filename of a DBase,
- Paradox or ODBC table(which must already be opened before you
- call the TblInfoDlg object) in the TABLE property field or at run
- time with code similar to this in your application:
-
- Table1 : TTable;
- TblInfoDlg1 : TTblInfoDlg;
-
- Table1.open;
- {Note that the table MUST be open before
- calling the TblInfoDlg object}
-
- TblInfoDlg1.table := table1;
- TblInfoDlg1.execute;
-
- If the TTable object you pass to TblInfoDlg is invalid or inactive, an
- error message pops up and it cleans up and terminates. Additional
- properties allow easy runtime access to the dialog's font, color and
- caption. Any change made in the font or color is also made for all of
- the dialog's subcomponents.
-
-
- Why put form properties in a wrapper unit?
- __________________________________________________________
-
- Good question. Delphi's architecture makes it very easy for the
- component user to change the properties of the 'wrapped' form
- (in this case, Tbldlg.pas)visually in the Object Inspector. That's
- great for design time, but some properties are useful to have around
- at run time. Since the TblDlg form is owned by the TblInfo unit,
- the user can access it's properties at runtime, but it would take
- some classically convoluted OOP code to do it. For example, by placing
- all the code needed to change the fonts of TblDlg and it's subcomponents
- into the wrapper unit, it takes the component user one line of code to do
- what otherwise would have taken 20 or 30 lines(this is also the reasoning
- behind the Parentxxx properties in Borland's components). In general, it
- helps to know how to add properties to a given component-non-visual
- components like TblInfoDlg(descended from TComponent) don't inherit
- very much in the way of properties. This listing shows all the code
- needed to let the user set TblDlg's font through the wrapper:
-
- interface
-
- type
-
- private
- fFont: TFont;
- procedure DlgFont(value : TFont);
-
- published
- property Font : TFont read FFont write DlgFont;
- {you don't specify a default value here-the program
- will use either the font defined in object inspector
- or the system default font}
-
- var
- MultPageDlg: TMultPageDlg;{the TblInfoDlg dialog form}
-
- implementation
-
- constructor TTblInfoDlg.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- fFont := TFont.Create;
- {the font and all other added property fields are
- initialized to default values}
-
-
- function TTblInfoDlg.Execute : Boolean;
- begin
- MultPageDlg := TMultPageDlg.Create(Application);
- MultPageDlg.Font := fFont;
- {this initializies the TblInfoDlg dialog and sets it's font
- to the default value}
-
- procedure tTblInfoDlg.DlgFont(value : TFont);
- begin
- if FFont <> Value then
- begin
- FFont.Assign(Value);
- {here is where the wrapper unit changes the value in the
- font property field when the user changes the value in the
- object inspector}
-
- Note that when you install the component, it defaults to whatever your
- system font is. Everybody's tastes are different, but I think it looks
- best using MsSanSerif, 8, Bold. You can adjust this in TTblInfoDlg's
- Font property at design time.
-
- The pattern is pretty much the same for the other properties depending
- on the property's data type.
-
-
- Why use BDE in Delpi Applications?
- ___________________________________
-
- With the exception of a few omissions , Delphi's implementation of BDE
- will serve you pretty well. If however, you are developing anything out
- of the ordinary, you'll eventually have to use BDE functions to fill in
- the gaps in Delphi's simplified(but very neatly encapsulated) version of
- BDE. To use BDE directly, you have to include DBIPROCS and DBITYPES in
- your USES declaration.
-
- Then there's the size issue-Delphi programs can get huge(I shouldn't
- talk..using the TblInfoDlg component will add about 80k to a program,
- big even by Delphi standards). Judicious use of BDE calls instead of
- adding and creating more components can substantially cut down on the
- size, load time and memory overhead of your exe's.
-
- Dbbrowse(in delphi\demos\db\tools) is the only Delphi demo program with
- direct calls to BDE. Borland takes the approach of creating 4 components
- to handle the 4 BDE calls used in this program. While this approach works
- fine, it's not mandatory (the advantages to this approach are simplicity
- and the fact that Delphi handles most exceptions). Calling BDE functions
- from your program instead of componentizing them is a little harder, but
- could result in significant gains in speed and smaller exe size. With good
- error checking, any BDE function will work safely in a Delphi program, and a
- number of them are used in the procedures GetTableDate_Size, DisplayFields
- and Display Indexes. With the exception of GetTableDate_Size, the same
- information could have been gathered from Delphi's component methods and
- data structures, but the required overhead would be much greater considering
- that this is just a dialog that displays the stuff and disappears. In the
- case of GetTableDate_Size, the choice is between using a specialized BDE
- function like dbiOpenTableList(which walks the BDE handle chain to get
- physical file information that was stored at the time the file was opened)
- or using the runtime library routines FileSize and FileGetDate(which close
- and reset the file and could wreak all sorts of havoc on the Delphi data
- controls that are linked to the table). The former just seems simpler and
- safer.
-
- BDE Error Checking
- ___________________
-
- Anyone who has had the experience of watching their Database program set off
- a long chain of excepetions and crash has probably learned this the hard way
- and can skip this part. Those who are new to BDE, though, might take some well
- meaning advice. Exception handling is an area where Delphi truly shines, making
- the Visual Basics and PowerBuilders of the world look feeble by comparison. When
- you use a Data Component, you get the benefit of Delphi's strong exception and
- error handling, but if you use direct BDE calls, you have to provide your own.
-
- Let's look at Dbbrowse again-the bdetable.pas unit(which contains the 3 BDE
- calls) wraps the calls in a CHECK routine which uses BDE's error stack to
- return the success or failure of direct BDE calls. While this is adequate
- for debugging and development, all you're doing here is getting information
- on whether your call went ok or why it failed. If the call DID fail, CHECK
- returns the error value and the program merrily goes on to the next call.
- If for example, the failed BDE call was supposed to return a cursor handle
- (hdbicur), the subsequent calls which access the data fields pointed to by
- this cursor will return a nil pointer(if you're lucky) or fail. To see this
- in action, load the Dbbrowse project(you have to install the three components
- in bdetable.pas first) and use the IDE Run command to run the Dbbrowse program.
- If you choose the View Table command without having the outline cursor on a
- table, you'll see a whole chain of error messages and then a GP fault as the
- BDE functions repeatedly try to access data in a nonexistent table.
-
- What we're trying to say here is that there are many cases whre your function
- that uses BDE calls must clean up and bail out if the call wasn't successful.
- Calls which return a handle of any type (like DbiOpenTableList, DbiOpenIndexList,
- or DbiOpenFieldList) fall into this category. Calls which return pointers to
- BDE data structures(like dbiGetCursorProps) can also be dangerous if execution
- continues after they fail. Table and field level calls like DBIGetNext Record
- or DBIGetField will simply return nil pointers or empty buffers if they fail-
- this alone won't cause your program to crash, but it won't endear you to the
- users of your program when they see garbage or blank fields instead of data.
-
- This is the reason for the paranoid-type code in DisplayIndexes, DisplayFields
- and GetTableDate_Size. To use BDE's error stack, you need to include DBIERRS in
- your uses clause. All BDE functions return DBIERR_NONE if successful and the
- three functions just mentioned show how to utilize this return value. Here's
- an example:
-
- var
- hCur : hDBICur;{BDE Cursor handle}
- name : array[0..80]of Char;{names passed to BDE functions can't be strings}
- recbuf :pbyte;{record buffer}
- tblprops :curprops;{table properties struct}
-
- begin
- recs := table.FieldCount-1;
- strPcopy(name,Table.TableName);
-
- {get a cursor handle to the field list table}
- if DbiOpenFieldList(table.DBHandle, name, nil, False, hCur) = DBIERR_NONE then
- {call was successful-continue}
-
- begin
- if dbiGetCursorProps(hCur,tblprops)= DBIERR_NONE then
- begin
- {call was successful-continue}
-
- dbiSetToBegin(hCur);
- for i := 0 to recs do
- begin
- {$I+}
- {use Delphi's exception handling routine to handle failed allocations gracefully}
- try
- GetMem(RecBuf, tblProps.iRecBufSize*sizeof(BYTE));
- except
- on EOutOfMemory do
- {bail out and clean up}
- begin
- DbiCloseCursor(hCur);
- {show error message here if you want}
- exit;
- end;
- {$I-}
- end;
- {various record and field level calls here}
- if recbuf <> nil then freemem(recbuf, tblprops.iRecBufSize * sizeof(BYTE));
- end;
- DbiCloseCursor(hCur);
- end
- else {call to dbiGetCursorProps failed-bail out and clean up}
- begin
- DbiCloseCursor(hCur);
- {show error message}
- end;
- end
- else ;{call to DbiOpenFieldList failed-show error message here}
-
- Borland's advice that you get the BDE devleloper's guide if you want to do
- this kind of stuff is very well taken. Contact them for info if you want this-
- I've found the Borland people on their various CIS development forums to be
- quite helpful.
-
- Anyway, I've said enough-you're better off looking at the code. Feel free to
- use this component in your programs or do whatever you like with it.
- Comments, improvements, opposing points of view, questions and the like are
- always welcome.
-
-
- Scott Hanrahan
- SJHDesign, INC
- CIS 70144,3033
-
-
- PS : The lawyer said I should always put in a disclaimer, even for sample code
- uploaded for the edification of others. This is example code for instructional
- purposes only and we do not make any warranties as to it's quality or suitability
- for your purposes-that's up to you to determine.
-
- Enjoy!
-